package com.cyx.gametool.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.cyx.gametool.cons.ExcelDataType;
import com.cyx.gametool.cons.ExcelGroup;
import com.cyx.gametool.excel.ExcelColumnTitle;
import com.cyx.gametool.excel.ExcelTableData;
import com.cyx.gametool.mgr.ControllerMgr;
import com.cyx.gametool.view.TextAreaPrintLog;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.util.converter.IntegerStringConverter;
import lombok.Getter;
import org.apache.poi.ss.extractor.ExcelExtractor;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;

public class ExcelConfigController extends AbsBaseController {

    @FXML
    private TextArea printfLog;

    @Getter
    private TextAreaPrintLog myPrintfLog;

    @FXML
    private TableView<?> tableTypeData;

    @FXML
    private Label fileCount;


    @FXML
    private TextField excelPath;

    @FXML
    private TextField excel_1;

    @FXML
    private TextField excel_2;

    @FXML
    private TextField excel_3;

    @FXML
    private TextField excel_4;

    @FXML
    private TextField excel_5;

    @FXML
    private TextField excel_6;

    private TextField[] excelConfigGroup;

    @FXML
    private CheckBox checkMysql;

    @FXML
    private CheckBox checkJava;

    @FXML
    private CheckBox checkJson;

    @FXML
    public void initialize() {
        System.out.println("excel_1 的初始值是: " + excel_1.getText());
        System.out.println("excel_2 的初始值是: " + excel_2.getText());
        myPrintfLog = new TextAreaPrintLog(printfLog);
        excelConfigGroup = new TextField[]{excel_1, excel_2, excel_3, excel_4, excel_5, excel_6};

        // 配置文件路径输入框监听内容改变
        excelPathAddListener();

        // 设置表格字段行数只能输入数字
        inputNumberOnly();

        addLostFocusListener(excelPath, excel_1, excel_2, excel_3, excel_4, excel_5, excel_6, checkMysql, checkJava, checkJson);
    }

    /**
     * 执行按钮点击事件
     *
     * @param event
     */
    @FXML
    void onButtonExecute(MouseEvent event) {
        // 获取文件路径
        String path = excelPath.getText();
        File file = new File(path);
        if (StrUtil.isEmpty(path)) {
            myPrintfLog.errorTextFieldLog(excelPath, "请输入配置文件路径或文件夹");
            return;
        } else if (!file.exists()) { // 如果文件不存在
            myPrintfLog.errorTextFieldLog(excelPath, "所选路径或文件不存在");
            return;
        }
        List<File> files = new ArrayList<>();
        // 如果是单个文件
        if (file.isFile()) {
            myPrintfLog.addInfoLog("选择一个配置文件【{}】", file.getName());
            files = List.of(file);
        } else if (file.isDirectory()) {
            // 文件夹下所有文件
            File[] directoryFiles = file.listFiles();
            if (directoryFiles == null) {
                myPrintfLog.addErrorLog("文件夹为空");
                return;
            }
            // 过滤出Excel文件 获取文件的数量
            files = Arrays.stream(directoryFiles).filter(ExcelConfigController.this::isExcelFile).toList();
            myPrintfLog.addInfoLog("共找到{}个Excel文件", files.size());
        }
        // 解析Excel文件
        try {
            // 解析Excel文件
            List<ExcelTableData> excelTableData = parseExcel(files);
            // 转化文件
            if (CollUtil.isNotEmpty(excelTableData)) {
                excelToThing(excelTableData, checkMysql, checkJava, checkJson);
            }
        } catch (Exception e) {
            myPrintfLog.addErrorLog("解析Excel文件失败：" + e.getMessage());
        }
    }

    /**
     * 解析Excel文件
     *
     * @param excelFiles Excel文件列表
     * @return
     */
    public List<ExcelTableData> parseExcel(List<File> excelFiles) {
        if (CollUtil.isEmpty(excelFiles)) {
            myPrintfLog.addErrorLog("所选择的文件夹下没有Excel文件");
            return null;
        }
        List<ExcelTableData> excelTableDataList = new ArrayList<>();
        // 获取Excel配置信息
        Integer fieldDesRow = Convert.toInt(excel_1.getText());
        Integer fieldNameRow = Convert.toInt(excel_2.getText());
        Integer fieldTypeRow = Convert.toInt(excel_3.getText());
        Integer importGroupsRow = Convert.toInt(excel_4.getText());
        Integer isNullRow = Convert.toInt(excel_5.getText());
        Integer dataStartRow = Convert.toInt(excel_6.getText());
        if (fieldDesRow == null || fieldNameRow == null || fieldTypeRow == null || importGroupsRow == null || isNullRow == null || dataStartRow == null) {
            myPrintfLog.addErrorLog("Excel配置信息不能为空");
            return null;
        }
        TimeInterval timer = DateUtil.timer();

        // 解析成功的文件数量
        int successCount = 0;
        ExecutorService excelParse = ThreadUtil.newFixedExecutor(20, "ExcelParse", true);
        // 遍历所有文件，解析Excel文件为ExcelTableData
        for (File file : excelFiles) {
            // 解析Excel文件为ExcelTableData
            ExcelTableData excelTableData = parseExcelFileToTableData(file, fieldDesRow, fieldNameRow, fieldTypeRow, importGroupsRow, isNullRow, dataStartRow);
            if (excelTableData == null) {
                myPrintfLog.addErrorLog("配置文件【{}】解析失败！请检查后重试！", file.getName());
                break;
            }
            successCount++;
            excelTableDataList.add(excelTableData);

//            ThreadUtil.execAsync(() -> {
//                ExcelTableData excelTableData = parseExcelFileToTableData(file, fieldDesRow, fieldNameRow, fieldTypeRow, importGroupsRow, isNullRow, dataStartRow);
//                if (excelTableData != null) {
//                    excelToThing(excelTableData, checkMysql, checkJava, checkJson);
//                }
//            });
        }
        long interval = timer.interval();//花费毫秒数
        if (successCount == excelFiles.size()) {
            myPrintfLog.addInfoLog("成功解析{}个Excel配置文件！！！,一个花费{}毫秒", successCount, interval);
            return excelTableDataList;
        }
        return null;
    }

    /**
     * excel数据转化为java代码 或者 json数据 或者mysql建表语句
     *
     * @param excelTableDataList
     * @param checkMysql
     * @param checkJava
     * @param checkJson
     */
    private void excelToThing(List<ExcelTableData> excelTableDataList, CheckBox checkMysql, CheckBox checkJava, CheckBox checkJson) {
        if (CollUtil.isEmpty(excelTableDataList)) {
            return;
        }
        for (ExcelTableData excelTableData : excelTableDataList) {
            if (checkMysql.isSelected()) {
                try {
                    // 转化为mysql建表语句
                    myPrintfLog.addInfoLog("开始转化配置文件【{}】为mysql建表语句", excelTableData.getSourceFile().getName());
                    // 转化为mysql建表语句
                    ControllerMgr.getInstance().getExcelToMysqlController().excelToMysqlSql(excelTableData);
                } catch (Exception e) {
                    myPrintfLog.addErrorLog("配置文件【{}】转化为java类失败！请检查后重试！", excelTableData.getSourceFile().getName());
                }
            }
            if (checkJava.isSelected()) {
                try {
                    // 转化为java类
                    myPrintfLog.addInfoLog("开始转化配置文件【{}】为java类", excelTableData.getSourceFile().getName());
                    // 转化为java类
                    ControllerMgr.getInstance().getExcelToJavaController().excelToJavaClass(excelTableData);
                } catch (Exception e) {
                    myPrintfLog.addErrorLog("配置文件【{}】转化为java类失败！请检查后重试！", excelTableData.getSourceFile().getName());
                }
            }
            if (checkJson.isSelected()) {
                try {
                    // 转化为json数据
                    myPrintfLog.addInfoLog("开始转化配置文件【{}】为json数据", excelTableData.getSourceFile().getName());
                    // 转化为json数据
                    ControllerMgr.getInstance().getExcelToJsonController().excelToJson(excelTableData);
                } catch (Exception e) {
                    myPrintfLog.addErrorLog("配置文件【{}】转化为java类失败！请检查后重试！", excelTableData.getSourceFile().getName());
                }
            }
        }
    }

    /**
     * 解析Excel文件为ExcelTableData
     *
     * @param file            文件对象
     * @param fieldDesRow     字段描述行
     * @param fieldNameRow    字段名行
     * @param fieldTypeRow    字段类型行
     * @param importGroupsRow 转换分组行
     * @param isNullRow       是否能为null行
     * @param dataStartRow    数据开始行
     * @return ExcelTableData
     */
    private ExcelTableData parseExcelFileToTableData(File file, Integer fieldDesRow, Integer fieldNameRow, Integer fieldTypeRow, Integer importGroupsRow, Integer isNullRow, Integer dataStartRow) {
        if (!isExcelFile(file)) {
            return null;
        }
        ExcelTableData tableData = new ExcelTableData();
        tableData.setSourceFile(file);


        ExcelReader reader = ExcelUtil.getReader(file, 0);
        String sheetName = reader.getSheet().getSheetName();
        tableData.setSheetName(sheetName);
        ExcelExtractor extractor = reader.getExtractor();
        List<List<Object>> allData = reader.read();

        // 字段名
        List<Object> names = allData.get(fieldNameRow - 1);
        // 描述
        List<Object> nameDesc = allData.get(fieldDesRow - 1);
        // 类型
        List<Object> nameType = allData.get(fieldTypeRow - 1);
        // 转换分组
        List<Object> nameToGroup = allData.get(importGroupsRow - 1);
        // 是否能为null
        List<Object> isNull = allData.get(isNullRow - 1);

        tableData.setExcelDataList(allData);
        tableData.setStartRowIndex(dataStartRow);

        // 用来存一张表的所有前几行信息字段
        List<ExcelColumnTitle> excelColumnTitles = new ArrayList<>();
        tableData.setExcelColumnTitles(excelColumnTitles);

        for (int index = 0; index < names.size(); index++) {
            // 字段名
            String name = String.valueOf(names.get(index));
            if (name.equals("null")) {
                continue;
            }
            // 描述
            String desc = String.valueOf(nameDesc.get(index));
            // 类型
            String typeStr = String.valueOf(nameType.get(index));
            // 是否为数组
            boolean isArray = typeStr.endsWith("[]");
            String typeStr1 = typeStr;
            if (isArray) {
                typeStr1 = typeStr.substring(0, typeStr.length() - 2);
            }
            // 类型
            ExcelDataType type = ExcelDataType.getByType(typeStr1);
            if (type == null) {
                myPrintfLog.addErrorLog("配置文件【{}】中有不支持的类型，请检查后重试！所在列：{}，类型：{}", file.getName(), index + 1, typeStr);
                return null;
            }
            // 转换分组
            List<ExcelGroup> groupList = Convert.toList(ExcelGroup.class, String.valueOf(nameToGroup.get(index)).toUpperCase());
            // 是否能为null
            // String支持的值为：true、false、yes、ok、no，1,0,,,,,,true表示能为null，false表示不能为空
            Boolean isNullFlag = Convert.toBool(isNull.get(index), false);

            excelColumnTitles.add(new ExcelColumnTitle(index, name, type, desc, groupList, isNullFlag));

            for (int startRowIndex = dataStartRow - 1; startRowIndex < allData.size(); startRowIndex++) {
                Object object = allData.get(startRowIndex).get(index);
                if (!type.checkType(isArray, object)) {
                    myPrintfLog.addErrorLog("配置文件【{}】中存在配置错误的单元格，请检查后重试！单元格所在行：{}，所在列：{}, 类型：{}", file.getName(), startRowIndex + 1, index + 1, typeStr);
                    return null;
                }

            }
            // 打印日志
            myPrintfLog.addInfoLog("字段名: {}, 描述: {}, 类型: {}, 转换分组: {}, 是否能为null: {}", name, desc, type, Convert.toStr(groupList), isNullFlag);
        }
        if (CollUtil.isEmpty(excelColumnTitles)) {
            myPrintfLog.addErrorLog("配置文件【{}】中没有有效的字段信息，请检查后重试！", tableData.getSourceFile().getName());
            return null;
        }
        return tableData;
    }

    /**
     * 选择文件或者文件夹
     *
     * @param actionEvent
     */
    @FXML
    public void onChooseDirectory(ActionEvent actionEvent) {
        // 创建一个文件夹选择器
        DirectoryChooser directoryChooser = new DirectoryChooser();
        directoryChooser.setTitle("选择配置文件所在文件夹");
        // 获取上次路径记录
        File lastFile = getInitialDirectory(excelPath);
        if (lastFile != null) {
            directoryChooser.setInitialDirectory(lastFile);
        }
        // 选择文件夹
        File selectedDirectory = directoryChooser.showDialog(null);
        if (selectedDirectory != null) {
            excelPath.setText(selectedDirectory.getAbsolutePath());
            checkFileCount();
        }
    }

    /**
     * 选择文件或者文件夹
     *
     * @param actionEvent
     */
    @FXML
    public void onChooseFiles(ActionEvent actionEvent) {
        // 创建一个文件选择器
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("选择配置文件");
        // 获取上次路径记录
        File lastFile = getInitialDirectory(excelPath);
        if (lastFile != null) {
            fileChooser.setInitialDirectory(lastFile);
        }
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Excel文件", "*.xls", "*.xlsx"));
        // 显示文件选择对话框
        File selectedFile = fileChooser.showOpenDialog(null);
        if (selectedFile != null) {
            excelPath.setText(selectedFile.getAbsolutePath());
            checkFileCount();
        }
    }

    /**
     * 只能输入一位整数
     */
    private void inputNumberOnly() {
        for (int index = 1; index <= excelConfigGroup.length; index++) {
            TextField textField = excelConfigGroup[index - 1];
            if (textField == null) {
                continue;
            }
            // 设置 TextField 只能输入数字
            textField.setTextFormatter(new javafx.scene.control.TextFormatter<>(new IntegerStringConverter(), index, change -> {
                String newText = change.getControlNewText();
                if (ReUtil.isMatch("[0-9]?", newText)) { // 允许输入整数
                    return change;
                } else {
                    return null;
                }
            }));
        }
    }

    /**
     * 配置文件路径输入框内容改变,需要获取路径中的文件数量
     */
    private void excelPathAddListener() {
        // 配置文件路径输入框监听失去焦点事件
        excelPath.focusedProperty().addListener((observable, oldValue, newValue) -> {
            if (!newValue) { // 失去焦点
                checkFileCount();
            }
        });
    }

    /**
     * 设置指定文件或文件夹的所在路径
     *
     * @return
     */
    public File getInitialDirectory(TextField textFieldPath) {
        String lastPath = textFieldPath.getText();
        if (StrUtil.isNotEmpty(lastPath)) {
            File lastFile = new File(lastPath);
            if (lastFile.exists()) {
                if (lastFile.isFile()) {
                    return lastFile.getParentFile();
                }
                if (lastFile.isDirectory()) {
                    return lastFile;
                }
            }
        }
        return null;
    }

    /**
     * 判断文件是否为Excel文件
     *
     * @param file 文件对象
     * @return 如果是Excel文件返回true，否则返回false
     */
    private boolean isExcelFile(File file) {
        String fileName = file.getName();
        return fileName.endsWith(".xlsx") || fileName.endsWith(".xls");
    }

    /**
     * excelPath内容改变检查所选的文件数量
     */
    private void checkFileCount() {
        String path = excelPath.getText();
        if (StrUtil.isNotEmpty(path)) {
            File file = new File(path);
            if (file.exists()) {
                if (file.isFile()) {
                    if (isExcelFile(file)) {
                        fileCount.setText("1");
                    } else {
                        fileCount.setText("0");
                    }
                }
                if (file.isDirectory()) {
                    try {
                        File[] files = file.listFiles();
                        assert files != null;
                        long count = Arrays.stream(files).filter(ExcelConfigController.this::isExcelFile).count();
                        fileCount.setText(String.valueOf(count));
                    } catch (SecurityException e) {
                        fileCount.setText("0");
                        myPrintfLog.addInfoLog("安全管理器阻止访问目录: " + path);
                    }
                }
            } else {
                myPrintfLog.addInfoLog("所选路径不存在：" + path);
            }
        } else {
            fileCount.setText("0");
        }
    }


    @Override
    protected void lostFocus(Control controls) {
        if (controls instanceof TextField textField) {
            System.out.println("TextField失去焦点+" + textField.getId() + ",,,," + textField.getText());
        }
    }
}
